#include <chrono>
#include <ctime>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>

#include "logformatter.h"
#include "loglevel.h"

namespace vlog
{

// %m
class MessageFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit MessageFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventMessageText()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %p 日志级别
class LevelFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit LevelFormatItem([[maybe_unused]] const std::string& str)
    {
    }

    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel())
           << LogLevel::ToString(pEvent->GetEventLogLevel()) << vlog::LogLevel::GetLevelColor();
    }
};

// %r
class RunTimeFormatTime : public LogFormatter::FormatItemImpl
{
    public:
    explicit RunTimeFormatTime([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventRunTime()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %c
class LoggerNameFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit LoggerNameFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventLoggerName()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %t
class ThreadIDFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit ThreadIDFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventThreadID()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %N
class ThreadNameFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit ThreadNameFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventThreadName()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %d{%Y-%m-%d %H:%M:%S}
class DataTimeFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit DataTimeFormatItem(const std::string& formatter = "%Y-%m-%d %H:%M:%S") : m_formatter(formatter)
    {
        if (m_formatter.empty())
        {
            m_formatter = "%Y-%m-%d %H:%M:%S";
        }
    }

    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        std::time_t curTimeMs = pEvent->GetEventTime();
        if constexpr (__cplusplus >= 201103L)
        { // C++11
            std::tm localtime_tm = *std::localtime(&curTimeMs);
            os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel())
               << std::put_time(&localtime_tm, m_formatter.c_str()) << vlog::LogLevel::GetLevelColor();
        }
        else
        {
            std::tm localtm;
            ::localtime_r(&curTimeMs, &localtm);
            char buf[64];
            strftime(buf, sizeof(buf), m_formatter.c_str(), &localtm);
            os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << buf << vlog::LogLevel::GetLevelColor();
        }
    }

    private:
    std::string m_formatter;
};

// %f 文件名
class FileNameFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit FileNameFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventFileName()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %l 行号
class FileLineFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit FileLineFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << pEvent->GetEventFileLine()
           << vlog::LogLevel::GetLevelColor();
    }
};

// %n 换行
class NewLineFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit NewLineFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << "\n";
    }
};
// %T 制表符
class TabFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit TabFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << "\t";
    }
};
// %% 百分号
class ParcentSignFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit ParcentSignFormatItem([[maybe_unused]] const std::string& str)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << "%" << vlog::LogLevel::GetLevelColor();
    }
};

// [] ...
class StringFormatItem : public LogFormatter::FormatItemImpl
{
    public:
    explicit StringFormatItem([[maybe_unused]] const std::string& message) : m_Message(message)
    {
    }
    void LogItemFormat(std::ostream& os, event::LogEvent::ptr pEvent) override final
    {
        os << vlog::LogLevel::GetLevelColor(pEvent->GetEventLogLevel()) << m_Message << vlog::LogLevel::GetLevelColor();
    }

    private:
    std::string m_Message;
};

LogFormatter::LogFormatter(const std::string& strPatter) : m_Pattern(strPatter)
{
    Init();
}

std::string LogFormatter::Formatter(event::LogEvent::ptr pEvent)
{
    std::stringstream ss;
    for (const auto& item : m_Items)
    {
        item->LogItemFormat(ss, pEvent);
    }
    return ss.str();
}

std::ostream& LogFormatter::Formatter(std::ostream& os, event::LogEvent::ptr pEvent)
{
    std::lock_guard lock(m_mutex);
    for (const auto& item : m_Items)
    {
        item->LogItemFormat(os, pEvent);
    }
    return os;
}

void LogFormatter::Init() noexcept
{

    // 按顺序存储解析到的pattern
    // 每个pattern 包括 一个整数类型和字符串， 0 是常规字符串,1 标识需要转义
    // 日志时间单独使用 dataformat
    std::vector<std::pair<int, std::string>> patterns;

    std::string tmp; // 临时存储常规字符串

    std::string dataformat; // 日志格式：默认%d后{} 的全部字符串当作格式字符,不校验是否合法

    // 是否解析错误
    bool isError = false;
    // 是否在解析常规字符串 初始true, %后的是模板字符
    bool isParsingString = true;
    // %d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%T[%p]%T[%c]%T%f:%l%T%m%n
    size_t i = 0;
    auto patternLen = m_Pattern.size();
    while (i < patternLen)
    {
        char c = m_Pattern[i];
        if ('%' == c)
        {
            if (isParsingString)
            {
                if (!tmp.empty())
                {
                    patterns.push_back(std::make_pair(0, tmp));
                }
                tmp.clear();
                isParsingString = false;
                ++i;
                continue;
            }
            else
            {
                patterns.push_back(std::make_pair(1, std::string(1, c)));
                isParsingString = true; // 在解析模板字符时遇到% 表示这是一个%转义
                ++i;
                continue;
            }
        }
        else
        {
            if (isParsingString) //持续解析常规字符知道遇到%, 接触出的字符串作为一个常规字符串加入patterns
            {
                tmp += c;
                ++i;
                continue;
            }
            else
            {
                // 模板字符串 直接继而patterns 添加完成后状态编程解析常规字符串， %d特殊处理
                patterns.push_back(std::make_pair(1, std::string(1, c)));
                isParsingString = true;

                // 对%d的特殊处理
                if ('d' != c)
                {
                    ++i;
                    continue;
                }
                ++i; // {
                if (i < patternLen && m_Pattern[i] != '{')
                {
                    continue;
                }
                ++i; // %
                while (i < patternLen && m_Pattern[i] != '}')
                {
                    dataformat.push_back(m_Pattern[i]);
                    ++i;
                }
                if (m_Pattern[i] != '}')
                {
                    std::cout << "[ERROR] LogFormatter::Init() "
                              << "pattern [" << m_Pattern << "] '{' not closed" << std::endl;
                    isError = true;
                }
                ++i;
                continue;
            }
        }
    }

    if (isError)
    {
        m_isError = true;
        return;
    }

    // 模板解析结束之后剩余的常规字符也要算进去
    if (!tmp.empty())
    {
        patterns.push_back(std::make_pair(0, tmp));
        tmp.clear();
    }

    // for (auto& [type, val] : patterns)
    // {
    //     std::cout << "type: " << type << "  value: " << val << std::endl;
    // }

    // std::cout << dataformat << std::endl;

    static std::map<std::string, std::function<FormatItemImpl::ptr(const std::string& fmt)>> s_format_items = {
#define XX(str, CB)                                                                                                    \
    {                                                                                                                  \
#str, [](const std::string& fmt) { return FormatItemImpl::ptr(std::make_shared<CB>(fmt) /*new CB(fmt)*/); }    \
    }

        XX(m, MessageFormatItem),     // m:消息
        XX(p, LevelFormatItem),       // p:日志级别
        XX(c, LoggerNameFormatItem),  // c:日志器名称
        XX(r, RunTimeFormatTime),     // r:累计毫秒数
        XX(f, FileNameFormatItem),    // f:文件名
        XX(l, FileLineFormatItem),    // l:行号
        XX(t, ThreadIDFormatItem),    // t:线程号
        XX(N, ThreadNameFormatItem),  // N:线程名称
        XX(%, ParcentSignFormatItem), // %:百分号
        XX(T, TabFormatItem),         // T:制表符
        XX(n, NewLineFormatItem)      // n:换行符
#undef XX
    };

    for (auto& [type, value] : patterns)
    {
        if (type == 0)
        {
            m_Items.push_back(FormatItemImpl::ptr(std::make_shared<StringFormatItem>(value)));
        }
        else if (value == "d")
        {
            m_Items.push_back(FormatItemImpl::ptr(std::make_shared<DataTimeFormatItem>(dataformat)));
        }
        else
        {
            auto it = s_format_items.find(value);
            if (s_format_items.end() != it)
            {
                m_Items.push_back(it->second(value));
            }
            else
            {
                std::cout << "[ERROR] LogFormatter::Init() "
                          << "pattern: [" << m_Pattern << "]"
                          << "unknown format item:" << value << std::endl;
                isError = true;
            }
        }
    }
    if (isError)
    {
        m_isError = true;
        return;
    }
}

} // namespace vlog
